﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


namespace EstructurasDeDatos.EstructuraGrafo
{
    public class Grafo<T,G> : IEnumerable<T>
    {
        #region Variables Privadas
        private ListaNodos<T> conjuntoDeNodos;        // el conjunto de nodos en el grafo
        #endregion

        #region Constructores

        public Grafo() : this(null) { }

        public Grafo(ListaNodos<T> conjuntoDeNodos)
        {
            if (conjuntoDeNodos == null)
                this.conjuntoDeNodos = new ListaNodos<T>();
            else
                this.conjuntoDeNodos = conjuntoDeNodos;
        }

        #endregion

        #region Metodos

        #region Agregar

        #region AgregarNodo
        
        public void AgregarNodo(NodoGrafo<T,G> nodo)
        {
            conjuntoDeNodos.Add(nodo);
        }
      
        public void AgregarNodo(T value)
        {
            conjuntoDeNodos.Add(new NodoGrafo<T,G>(value));
        }

        #endregion

        #region Metodos de agregación de arcos
      
        public void AgregarArcoDirigido(T desde, T hasta)
        {
            AgregarArcoDirigido(desde, hasta, 0);
        }

        public void AgregarArcoDirigido(NodoGrafo<T,G> desde, NodoGrafo<T,G> hasta)
        {
            AgregarArcoDirigido(desde, hasta, 0);
        }

        public void AgregarArcoDirigido(NodoGrafo<T,G> desde, NodoGrafo<T,G> hasta, double costo)
        {
            desde.Vecinos.Add(hasta);
            desde.Costos.Add(costo);
        }

        public void AgregarArcoDirigido(T desde, T hasta, double costo)
        {
            ((NodoGrafo<T,G>)conjuntoDeNodos.FindByValue(desde)).Vecinos.Add(conjuntoDeNodos.FindByValue(hasta));
            ((NodoGrafo<T,G>)conjuntoDeNodos.FindByValue(desde)).Costos.Add(costo);
        }
       
        public void AgregarArcoNoDirigido(T desde, T hasta)
        {
            AgregarArcoNoDirigido(desde, hasta, 0);
        }

        public void AgregarArcoNoDirigido(NodoGrafo<T,G> desde, NodoGrafo<T,G> hasta)
        {
            AgregarArcoNoDirigido(desde, hasta, 0);
        }

        public void AgregarArcoNoDirigido(NodoGrafo<T,G> desde, NodoGrafo<T,G> hasta, double costo)
        {
            desde.Vecinos.Add(hasta);
            desde.Costos.Add(costo);

            hasta.Vecinos.Add(desde);
            desde.Costos.Add(costo);
        }

        public void AgregarArcoNoDirigido(T desde, T hasta, double costo)
        {
            ((NodoGrafo<T,G>)conjuntoDeNodos.FindByValue(desde)).Vecinos.Add(conjuntoDeNodos.FindByValue(hasta));
            ((NodoGrafo<T,G>)conjuntoDeNodos.FindByValue(desde)).Costos.Add(costo);

            ((NodoGrafo<T,G>)conjuntoDeNodos.FindByValue(hasta)).Vecinos.Add(conjuntoDeNodos.FindByValue(desde));
            ((NodoGrafo<T, G>)conjuntoDeNodos.FindByValue(hasta)).Costos.Add(costo);
        }
        #endregion

        #endregion

        #region Limpiar
       
        public void Limpiar()
        {
            conjuntoDeNodos.Clear();
        }
        #endregion

        #region Contains
      
        public bool Contains(T value)
        {
            return conjuntoDeNodos.FindByValue(value) != null;
        }
        #endregion
        
        #region Eliminar
       
        public bool Eliminar(T value)
        {
           
            NodoGrafo<T,G> nodoEliminar = (NodoGrafo<T,G>)conjuntoDeNodos.FindByValue(value);
            if (nodoEliminar == null)
                // no se encontro el nodo
                return false;

            // el nodo se encontro
            conjuntoDeNodos.Remove(nodoEliminar);

            // elimininar de cada nodo en el conjunto de nodos
            foreach (NodoGrafo<T,G> gnode in conjuntoDeNodos)
            {
                int index = gnode.Vecinos.IndexOf(nodoEliminar);
                if (index != -1)
                {
                    // eliminar las referencias al nodo
                    gnode.Vecinos.RemoveAt(index);
                }
            }

            return true;
        }
        #endregion

        #region IEnumerable<T> Members
       
        public IEnumerator<T> GetEnumerator()
        {
            foreach (NodoGrafo<T,G> gnode in conjuntoDeNodos)
                yield return gnode.Valor;
        }
        #endregion
        #endregion

        #region Propiedades Públicas
        
        public ListaNodos<T> Nodos
        {
            get
            {
                return conjuntoDeNodos;
            }
        }
      
        public int Cuenta
        {
            get { return conjuntoDeNodos.Count; }
        }
        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            throw new NotImplementedException();
        }

        #endregion
    }
}
